﻿#include"snake.h"

//设置光标的坐标
void SetPos(short x, short y)
{
	COORD pos = { x, y };
	HANDLE hOutput = NULL;
	//获取标准输出的句柄(⽤来标识不同设备的数值)
	hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	//设置标准输出上光标的位置为pos
	SetConsoleCursorPosition(hOutput, pos);
}

// 欢迎界面
void WelcomeToGame()
{
	SetPos(40, 16);
	wprintf(L"欢迎进入贪吃蛇游戏");
	SetPos(40, 28);
	system("pause");
	system("cls");
	SetPos(35, 15);
	wprintf(L"用 ↑↓←→ 来控制蛇的移动,F3加速，F4减速");
	SetPos(35, 16);
	wprintf(L"加速可获得更多分数");
	SetPos(40, 28);
	system("pause");
	system("cls");
}

//创建地图
void CreatMap()
{
	//上
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"%lc", WALL);
	}
	//下
	SetPos(0, 26);
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"%lc", WALL);
	}
	//左
	for (int i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}
	//右
	for (int i = 1; i <= 25; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", WALL);
	}
}

//创建蛇身
void InitSnake(Snake* snake)
{
	int i = 0;
	for (i = 0; i < 5; i++)//初始有5个蛇身
	{
		SnakeNode* newSnakeNode = (SnakeNode*)malloc(sizeof(SnakeNode));
		if (newSnakeNode == NULL)
		{
			perror("malloc() fail");
			return;
		}

		//每个蛇身的信息
		newSnakeNode->next = NULL;
		newSnakeNode->x = POS_X + i * 2;
		newSnakeNode->y = POS_Y;

		//头插法插入节点
		if (snake->pSnake == NULL)
		{
			snake->pSnake = newSnakeNode;
		}
		else
		{
			newSnakeNode->next = snake->pSnake;
			snake->pSnake = newSnakeNode;
		}
	}

	//打印蛇身
	SnakeNode* cur = snake->pSnake;
	for (i = 0; i < 5; i++)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	snake->dir = RIGHT;
	snake->food_weight = 10;
	snake->score = 0;
	snake->sleep_time = 200;
	snake->pStatus = OK;
}

//创建食物
void CreateFood(Snake* snake)
{
	int x = 0;
	int y = 0;
	//创建食物节点
	SnakeNode* food = (SnakeNode*)malloc(sizeof(SnakeNode));
	if (food == NULL)
	{
		perror("malloc()fail");
		return;
	}

again:
	do
	{
		x = rand() % 53 + 2;
		y = rand() % 25 + 1;
	} while (x % 2 != 0);

	SnakeNode* cur = snake->pSnake;
	while (cur)
	{
		if (x == cur->x && y == cur->y)
		{
			goto again;
		}
		cur = cur->next;
	}

	food->x = x;
	food->y = y;
	food->next = NULL;

	SetPos(x, y);
	wprintf(L"%lc", FOOD);

	snake->pFood = food;
}


void GameStart(Snake* snake)
{
	//0. 设置画面大小，隐藏光标
	system("mode con cols=100 lines=32");
	system("title 贪吃蛇");

	HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	//影藏光标操作
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
	CursorInfo.bVisible = false; //隐藏控制台光标
	SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态

	//1. 欢迎界面
	WelcomeToGame();

	//2. 创建地图
	CreatMap();

	//3. 创建蛇身,蛇的属性
	InitSnake(snake);
	
	//4. 创建食物
	CreateFood(snake);
}

//游戏信息
void GameInfo(Snake* snake)
{
	
	SetPos(68, 17);
	printf("F3加速，F4减速");
	SetPos(68, 18);
	printf("按ESC退出游戏，SPACE 暂停游戏");

}

void GameStop()
{
	while (1)
	{
		Sleep(200);
		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
	}
}

void SnakeFast(Snake* snake)
{
	if (snake->sleep_time > 80)
	{
		snake->sleep_time -= 20;
		snake->food_weight += 2;
	}
}

void SnakeSlow(Snake* snake)
{
	if (snake->sleep_time < 280)
	{
		snake->sleep_time += 20;
		snake->food_weight -= 2;
	}
}

int NextIsFood(Snake* snake, SnakeNode* NextNode)
{
	return snake->pFood->x == NextNode->x && snake->pFood->y == NextNode->y;
}

void EatFood(Snake* snake, SnakeNode* NextNode)
{
	NextNode->next = snake->pSnake;
	snake->pSnake = NextNode;

	SnakeNode* cur = snake->pSnake;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}

	free(snake->pFood);
	snake->pFood = NULL;

	snake->score += snake->food_weight;
	CreateFood(snake);
}

void NextNoFood(Snake* snake, SnakeNode* NextNode)
{
	NextNode->next = snake->pSnake;
	snake->pSnake = NextNode;

	SnakeNode* cur = snake->pSnake;
	while (cur->next->next)
	{
		cur = cur->next;
	}
	SetPos(cur->next->x, cur->next->y);
	printf("  ");
	free(cur->next);
	cur->next = NULL;

	cur = snake->pSnake;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
}

//撞墙
void kill_by_wall(Snake* snake)
{
	if (snake->pSnake->x == 0 || snake->pSnake->x == 56 
		|| snake->pSnake->y == 0 || snake->pSnake->y == 27)
	{
		snake->pStatus = KILL_BY_WALL;
	}
}

//撞到自己
void kill_by_self(Snake* snake)
{
	SnakeNode* cur = snake->pSnake->next;
	while (cur)
	{
		if (cur->x == snake->pSnake->x && cur->y == snake->pSnake->y)
		{
			snake->pStatus = KILL_BY_SELF;
			break;
		}
		cur = cur->next;
	}
}

void SnakeMode(Snake* snake)
{
	SnakeNode* NextNode = (SnakeNode*)malloc(sizeof(SnakeNode));
	if (NextNode == NULL)
	{
		perror("malloc()fail");
		return;
	}

	switch(snake->dir)
	{
	case UP:
		NextNode->x = snake->pSnake->x;
		NextNode->y = snake->pSnake->y - 1;
		break;
	case DOWN:
		NextNode->x = snake->pSnake->x;
		NextNode->y = snake->pSnake->y + 1;
		break;
	case LEFT:
		NextNode->x = snake->pSnake->x - 2;
		NextNode->y = snake->pSnake->y;
		break;
	case RIGHT:
		NextNode->x = snake->pSnake->x + 2;
		NextNode->y = snake->pSnake->y;
		break;
	}

	if (NextIsFood(snake, NextNode))
	{
		EatFood(snake, NextNode);
	}
	else
	{
		NextNoFood(snake, NextNode);
	}
	
	//撞墙
	kill_by_wall(snake);

	//撞到自己
	kill_by_self(snake);
}


//运行游戏
void GameRun(Snake* snake)
{
	//游戏信息
	GameInfo(snake);
	do
	{
		SetPos(68, 14);
		printf("一个食物的分数：%2d", snake->food_weight);
		SetPos(68, 15);
		printf("总分数：%d", snake->score);
		if (KEY_PRESS(VK_UP) && snake->dir != DOWN)
		{
			snake->dir = UP;
		}
		else if (KEY_PRESS(VK_DOWN) && snake->dir != UP)
		{
			snake->dir = DOWN;
		}
		else if (KEY_PRESS(VK_LEFT) && snake->dir != RIGHT)
		{
			snake->dir = LEFT;
		}
		else if (KEY_PRESS(VK_RIGHT) && snake->dir != LEFT)
		{
			snake->dir = RIGHT;
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			snake->pStatus = END_NORMAL;
		}
		else if (KEY_PRESS(VK_SPACE))
		{
			GameStop();
		}
		else if (KEY_PRESS(VK_F3))
		{
			SnakeFast(snake);
		}
		else if (KEY_PRESS(VK_F4))
		{
			SnakeSlow(snake);
		}

		//蛇的移动
		SnakeMode(snake);
		Sleep(snake->sleep_time);

	} while (snake->pStatus == OK);
}

//结束游戏
void GameEnd(Snake* snake)
{
	SetPos(25, 14);
	switch (snake->pStatus)
	{
	case END_NORMAL:
		printf("正常退出游戏");
		break;
	case KILL_BY_SELF:
		printf("撞到自己，游戏结束");
		break;
	case KILL_BY_WALL:
		printf("撞到墙，游戏结束");
		break;
	}

	SnakeNode* cur = snake->pSnake;
	while (cur)
	{
		SnakeNode* del = cur;
		cur = cur->next;
		free(del);
		del = NULL;
	}
}